use std::io::Read;
use std::fs;
use std::io::Write;
use std::path::Path;
use crate::AppResult;
use crate::context::certificate::{
    get_certificate_context,
};
use crate::context::{get_file_or_create, read_to_string};
use asn1::{ObjectIdentifier, SequenceWriter};
use const_format::concatcp;
use openssl::bn::{BigNum, BigNumContext};
use openssl::sha::{sha256};
use tokio::io::{AsyncWriteExt};
use tracing::{error, info};
use walkdir::WalkDir;
use x509_parser::certificate::X509Certificate;
use x509_parser::prelude::FromDer;
use zip::CompressionMethod::Stored;
use zip::write::{SimpleFileOptions};
use zip::ZipWriter;

pub const AGENT_CONTEXT_FILE_PATH: &str = "external/agent/ja-netfilter";
pub const AGENT_CONTEXT_FILE_ZIP_NAME: &str = "external/agent/ja-netfilter.zip";
const POWER_CONF_FILE_NAME_PATH: &str = concatcp!(AGENT_CONTEXT_FILE_PATH, "/config/power.conf");

pub async fn init() -> AppResult<()> {
    if !power_conf_has_init().await? {
        info!("配置初始化中....");
        generate_power_conf().await?;
        info!("配置初始化完成");
    }
    if !tokio::fs::try_exists(AGENT_CONTEXT_FILE_ZIP_NAME).await? {
        zip(AGENT_CONTEXT_FILE_PATH, AGENT_CONTEXT_FILE_ZIP_NAME)?;
    };
    Ok(())
}
 fn zip(folder_path: &str, zip_path: &str) -> AppResult<()> {
    let output_file = fs::File::create(&Path::new(zip_path))?;
    let walkdir = WalkDir::new(Path::new(folder_path));
    let it = walkdir.into_iter();

    let mut zip = ZipWriter::new(output_file);
    let file_options = SimpleFileOptions::default()
        .compression_method(Stored)
        .unix_permissions(0o755);

    for entry_res in it {
        let entry;
        match entry_res {
            Ok(r) => {
                entry = r;
            },
            Err(e) => {
                error!("{:?}", e);
                continue;
            }
        }

        let path = entry.path();


        let name = path.strip_prefix(Path::new(folder_path)).expect("path.strip_prefix error");
        if path.is_file() {
            println!("adding {:?} as {:?}", path, name);
            zip.start_file(name.to_string_lossy(), file_options)?;
            let mut f = fs::File::open(path)?;
            let mut buffer = Vec::new();
            f.read_to_end(&mut buffer)?;
            zip.write_all(&buffer)?;
        }
    }
    zip.finish()?;
    Ok(())
}
async fn power_conf_has_init() -> AppResult<bool> {
    let mut power_conf_file = get_file_or_create(POWER_CONF_FILE_NAME_PATH).await?;
    let string=read_to_string(&mut power_conf_file).await?;
    Ok(string.contains("[Result]") && string.contains("EQUAL,"))
}
async fn generate_power_conf() -> AppResult<()> {
    let cert_context = get_certificate_context().await;
    let code_crt = cert_context.cache().get_code_cet();
    let public_key = cert_context.cache().get_public_key();
    let root_public_key = cert_context.cache().get_code_root_crt().public_key()?.rsa()?;
    let x = BigNum::from_slice(code_crt.signature().as_slice())?;
    let y = 65537;
    let z = root_public_key.n();
    let mut num_context = BigNumContext::new()?;
    let mut r = BigNum::new()?;
    let rsa_public_key = public_key.rsa()?;
    r.mod_exp(&x, rsa_public_key.e(), rsa_public_key.n(), &mut num_context)?;
    let code_rule = format!("; Activation Code \nEQUAL,{},{},{}->{}", x, y, z, r);
    let server_child_crt = cert_context.cache().get_server_child_cet();
    let server_root_public_key = cert_context.cache().get_server_root_crt().public_key()?.rsa()?;
    let x1 = BigNum::from_slice(server_child_crt.signature().as_slice())?;
    let y1 = 65537;
    let z1 = server_root_public_key.n();
    let server_child_der = server_child_crt.to_der()?;
    let (_, cx) = X509Certificate::from_der(&server_child_der)?;
    let sha256_der = sha256(cx.tbs_certificate.as_ref());
    let transit = convert_data_to_asn1(sha256_der.as_ref())?;
    let transit = filling(transit, 512);
    let r1 = BigNum::from_hex_str(transit.as_ref())?;
    let server_child_rule = format!("{}\n; License Server \nEQUAL,{},{},{}->{}",code_rule, x1, y1, z1, r1);
    let power_conf = format!("[Result]\n{}", server_child_rule);
    let mut power_file = get_file_or_create(POWER_CONF_FILE_NAME_PATH).await?;
    power_file.write_all(power_conf.as_bytes()).await?;
    power_file.flush().await?;
    Ok(())
}
fn convert_data_to_asn1(data: &[u8]) -> AppResult<String> {
    let result = asn1::write(|x| {
        x.write_element(&SequenceWriter::new(&|w| {
            w.write_element(&SequenceWriter::new(&|w1| {
                w1.write_element(&ObjectIdentifier::from_string("2.16.840.1.101.3.4.2.1"))?;
                w1.write_element(&())?;
                Ok(())
            }))?;
            w.write_element(&data)?;
            Ok(())
        }))?;
        Ok(())
    });
    Ok(hex::encode(result?))
}
fn filling(target: String, length: usize) -> String {
    let count = length - target.len() / 2 - 3;
    let mut build_str = String::new();
    for _ in 0..count {
        build_str.push_str("ff");
    }
    format!("01{}00{}", build_str, target).to_uppercase()
}

#[cfg(test)]
mod tests {
    use super::*;
    #[tokio::test]
    async fn test_generate_power_conf()->AppResult< ()> {
        let cert_context = get_certificate_context().await;
        let code_crt = cert_context.cache().get_code_cet();
        let public_key = cert_context.cache().get_public_key();
        let root_public_key = cert_context.cache().get_code_root_crt().public_key()?.rsa()?;
        let x = BigNum::from_slice(code_crt.signature().as_slice())?;
        let y = 65537;
        let z = root_public_key.n();
        let mut num_context = BigNumContext::new()?;
        let mut r = BigNum::new()?;
        let rsa_public_key = public_key.rsa()?;
        r.mod_exp(&x, rsa_public_key.e(), rsa_public_key.n(), &mut num_context)?;
        let code_rule = format!("; Activation Code \nEQUAL,{},{},{}->{}", x, y, z, r);
        let server_child_crt = cert_context.cache().get_server_child_cet();
        let server_root_public_key = cert_context.cache().get_server_root_crt().public_key()?.rsa()?;
        let x1 = BigNum::from_slice(server_child_crt.signature().as_slice())?;
        let y1 = 65537;
        let z1 = server_root_public_key.n();
        let server_child_der = server_child_crt.to_der()?;
        let (_, cx) = X509Certificate::from_der(&server_child_der)?;
        let sha256_der = sha256(cx.tbs_certificate.as_ref());
        let transit = convert_data_to_asn1(sha256_der.as_ref())?;
        let transit = filling(transit, 512);
        let r1 = BigNum::from_hex_str(transit.as_ref())?;
        let server_child_rule = format!("{}\n; License Server \nEQUAL,{},{},{}->{}",code_rule, x1, y1, z1, r1);
        let power_conf = format!("[Result]\n{}", server_child_rule);
        println!("{}", power_conf) ;
        Ok(())
    }
}